package com.striker.imwebsocket;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import com.sun.deploy.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.db.domain.KefuConversation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;


@ServerEndpoint("/webSocket/{userid}")
@Component
public class WebSocketServer {
    private final Log logger = LogFactory.getLog(WebSocketServer.class);

    private static WebSocketService webSocketService;

    @Autowired
    public void setKefuService(WebSocketService webSocketService){
        WebSocketServer.webSocketService= webSocketService;
    }

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。//todo
    private static AtomicInteger onlineNum = new AtomicInteger();

    //concurrent包的线程安全Set，用来存放每个客户端对应的WebSocketServer对象。
    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    //某userId被某kefuId服务中(inservice)
    private static ConcurrentHashMap<String, String> userId2kefuId = new ConcurrentHashMap<>();

    //记录每个client上次的心跳时间
    private static ConcurrentHashMap<String, LocalDateTime> userId2pingtime = new ConcurrentHashMap<>();

    //发送消息
    public void sendMessage(Session session, String message) throws IOException {
        if(session != null){
            synchronized (session) {
                logger.info("发送数据：" + message);
                session.getBasicRemote().sendText(message);
            }
        }

    }

    //给指定用户发送信息
    public void sendInfo(String userName, String message){
        Session session = sessionPools.get(userName);
        try {
            sendMessage(session, message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    // 群发消息
    public void broadcast(String message){
        for (Session session: sessionPools.values()) {
            try {
                sendMessage(session, message);
            } catch(Exception e){
                e.printStackTrace();
                continue;
            }
        }
    }

    //建立连接成功调用
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userid") String userid){
        session.setMaxIdleTimeout(10* 60 * 1000);
        if(userid.contains("-")){//后台客服连过来 userid-kefuid的形式
            String [] strArr = userid.split("-");
            if(strArr != null && strArr.length == 2){
                String realUserId = strArr[0];
                String kefuId = strArr[1];
                userId2kefuId.put(realUserId, kefuId);//kefuId开始服务realUserId
                logger.info("开始服务|kefuId:" + kefuId + "|userId:"+realUserId);
                KefuConversation c = new KefuConversation();
                c.setUserid(Integer.valueOf(realUserId));
                c.setInservice((short) 1);//客服点进会话框, 更新状态
                c.setViewed((short)1);
                WebSocketServer.webSocketService.updateConversation(c);

                userid = kefuId;//为了函数尾部sessionPools对kefuId连接的长链接的管理:sessionPools.put(userid, session);
            }else {
                logger.error("后台客服连websocket格式错误");
            }
        }
        else {//给用户发欢迎词
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String time = (sdf.format(new Date()));
                sendMessage(session, "{\"time\":\"" + time + "\",\"addTime\":\""+time+"\",\"fromUserid\":\"" + 0 + "\",\"toUserid\":" + userid
                        + ",\"content\":\"" + "Hi，我是小甜甜，有什么问题可以直接提问，我会尽快答复您~" + "\",\"msgType\":\"" + "0" + "\"}");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(sessionPools.get(userid) != null){//在其他地方登录，踢之前的下线
            Session sessionOld = sessionPools.get(userid);
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String time = (sdf.format(new Date()));
                String offlineMsg = "{\"time\":\""+time+ "\",\"fromUserid\":\""+0+"\",\"toUserid\":"+userid
                        +",\"content\":\""+"你已在其他地方登录"+"\",\"msgType\":\""+"-1"+"\"}";
                sendMessage(sessionOld, offlineMsg);
            }catch (Exception e){
                e.printStackTrace();
            }
            sessionPools.remove(userid);
            userId2kefuId.remove(userid);
            userId2pingtime.remove(userid);
        }
        sessionPools.put(userid, session);
        userId2pingtime.put(userid, LocalDateTime.now());
        addOnlineCount();

        logger.info(userid + "加入webSocket！当前人数为" + onlineNum + "|pingTime:"+userId2pingtime.get(userid));
    }


    //关闭连接时调用
    @OnClose
    public void onClose(@PathParam(value = "username") String userName){
        sessionPools.remove(userName);
        subOnlineCount();

        //客服关闭连接
        for (String userId: userId2kefuId.keySet()) {
            String kefuId = userId2kefuId.get(userId);
            if(kefuId.equals(userName)){
                userId2kefuId.remove(userId);//userId not inservice now.
                logger.info(userId + " not inservice now.|之前kefuId:" + kefuId);
                break;
            }
        }
        logger.info(userName + "断开webSocket连接！当前人数为" + onlineNum);
    }

    // 收到客户端信息后，根据接收人的username把消息推下去或者群发
    // to=-1群发消息
    @OnMessage
    public void onMessage(String message){
        logger.info("server get" + message);
        KefuMessage msg = JSON.parseObject(message, KefuMessage.class);
        LocalDateTime now = LocalDateTime.now();
        msg.setAddTime(now);
        msg.setUpdateTime(now);
        if(msg.getMsgType().intValue() != -3) { //ping消息不入库
            WebSocketServer.webSocketService.insertMsg(msg);
        }

        if (msg.getToUserid().intValue() == -1) {
            broadcast(JSON.toJSONString(msg,true));
        } else if(msg.getToUserid().intValue() == 0){//用户发过来的
            //sendInfo(msg.getTo(), JSON.toJSONString(msg,true));
            logger.info("收到新会话|" + message);
            //用户、客服发过来的ping消息都要更新到达时间
            String userId = msg.getFromUserid().toString();
            if(msg.getMsgType() == -3){
                userId2pingtime.put(userId, LocalDateTime.now());
                logger.info("|pingTime:"+userId2pingtime.get(userId));
                sendPong(userId);
                return;
            }

            if(! StringUtils.isBlank(msg.getContent())) {
                KefuConversation c = new KefuConversation();
                c.setUserid(msg.getFromUserid());
                c.setMsgType(msg.getMsgType());
                c.setClientType(msg.getClientType());
                c.setContent(msg.getContent());
                c.setAddTime(now);
                c.setUpdateTime(now);
                c.setInservice(userId2kefuId.get(userId) == null ? (short) 0 : (short) 1);
                WebSocketServer.webSocketService.updateConversation(c);
            }

            if(userId2kefuId.get(userId) != null) {//inservice
                //下发给客服处理
                String kefuId = userId2kefuId.get(msg.getFromUserid().toString());
                logger.info("下发给客服处理|kefuId:" + kefuId +"|msg:" + message);
                sendInfo(kefuId, message);
            }
        } else {//客服发过来
            logger.info("收到新会话|" + message);
            String kefuId = msg.getFromUserid().toString();
            if(msg.getMsgType() == -3){
                userId2pingtime.put(kefuId, LocalDateTime.now());
                logger.info("kefuId:" + kefuId + "|pingTime:"+userId2pingtime.get(kefuId));
                sendPong(kefuId);
                return;
            }

            String userId = msg.getToUserid().toString();
            if(! StringUtils.isBlank(msg.getContent())) {
                KefuConversation c = new KefuConversation();
                c.setUserid(msg.getToUserid());
                c.setMsgType(msg.getMsgType());
                c.setClientType(msg.getClientType());
                c.setContent(msg.getContent());
                c.setAddTime(now);
                c.setUpdateTime(now);
                c.setInservice(sessionPools.get(userId) != null ? (short) 1 : (short) 0);
                WebSocketServer.webSocketService.updateConversation(c);
            }

            if(sessionPools.get(userId) != null){//用户在线
                logger.info("下发给用户|userId:" + userId +"|msg:" + message);
                sendInfo(userId, message);
            }
        }
    }

    private void sendPong(String userId){
        logger.info("下发pong msg给用户|userId:" + userId);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String pongMsg = "{\"fromUserid\":\""+0+"\",\"toUserid\":"+userId
                +",\"content\":\""+"pong"+"\",\"msgType\":\""+"-4"+"\"}";
        sendInfo(userId, pongMsg);
    }

    //错误时调用
    @OnError
    public void onError(Session session, Throwable throwable){
        logger.info("onError|sessionId:"+session.getId());
        String errorId = null;
        for(String userId:sessionPools.keySet()){
            if(session.equals(sessionPools.get(userId))){
                logger.info("找到userId:"+userId);
                errorId = userId;
                break;
            }
        }
        sessionPools.remove(errorId);
        subOnlineCount();

        //用户掉线时移除
        for (String userId: userId2kefuId.keySet()) {
            if(userId.equals(errorId)){
                logger.info(userId + " not inservice now.|之前kefuId:" + userId2kefuId.get(userId));
                userId2kefuId.remove(errorId);//userId not inservice now.
                break;
            }
        }
        //客服掉线时移除 todo
        for (String userId: userId2kefuId.keySet()) {
            String kefuId = userId2kefuId.get(userId);
            if(kefuId.equals(errorId)){
                logger.info(userId + " not inservice now.|之前kefuId:" + kefuId);
                userId2kefuId.remove(userId);//userId not inservice now.
                break;
            }
        }
        throwable.printStackTrace();
    }

    public static void addOnlineCount(){//todo map.size更准?
        onlineNum.incrementAndGet();
    }

    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }

    public static AtomicInteger getOnlineNumber() {
        return onlineNum;
    }

    public static ConcurrentHashMap<String, Session> getSessionPools() {
        return sessionPools;
    }

    /**
     * 每一分钟检查一次客户端心跳情况
     */
//    @Scheduled(cron="0 0/1 * * * ?")
//    private void sendAll(){
//         logger.info("每一分钟检查一次客户端心跳情况");
//        // 客户端每分钟心跳一次
//        for (String userId:userId2pingtime.keySet()) {
//            LocalDateTime pingTime = userId2pingtime.get(userId);
//            // 上次心跳时间离当前时间超过1.5分钟，踢下线
//            if(LocalDateTime.now().minusSeconds(120).isAfter(pingTime)) {
//                logger.warn("上次心跳时间离当前时间超过2分钟，踢下线|userId:" + userId + "|pingTime:"+pingTime);
////                try {
////                    sessionPools.get(userId).close();
////                }catch (IOException e){
////                    logger.error("关闭连接异常|"+e.toString());
////                }
//                sessionPools.remove(userId);
//
//                userId2pingtime.remove(userId);
//                userId2kefuId.remove(userId);
//                //subOnlineCount();
//            }
//        }
//    }
}

